package com.aegonthtf.sis.aspect;

import cn.com.common.util.domain.RequestStore;
import cn.com.common.util.request.HttpRequestUtils;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 日志切面
 *
 * @author huangjiangming@fulan.com.cn
 * @date 2021/4/13 19:34
 */
@Aspect
@Component
@Order(1)
@Slf4j
public class LogAspect {

    /**
     * 日志记录
     *
     * @param joinPoint 切入点
     * @return java.lang.Object
     * @author huangjiangming@fulan.com.cn
     * @date 2021/4/13 19:35
     */
    @Around("execution(* com.aegonthtf.sis..controller..*(..))")
    public Object logController(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();

        // 请求地址
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        sb.append("[ ").append(HttpRequestUtils.getRemoteIP(request)).append(" ] ");
        String method = request.getMethod();
        sb.append("[ ").append(request.getMethod()).append(" ] ");

        // 请求方法描述
        Signature signature = joinPoint.getSignature();
        ApiOperation apiOperation = ((MethodSignature) signature).getMethod().getAnnotation(ApiOperation.class);
        String msgDesc = StringUtils.isEmpty(apiOperation) ? signature.getName() : apiOperation.value();
        sb.append("[ ").append(msgDesc).append(" ] ");

        // 请求地址
        sb.append("[ 请求地址 ").append(request.getRequestURL().toString()).append(" ] ");
        // 请求参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        if (!parameterMap.isEmpty()) {
            Map<String, String> paramMap = new HashMap<>();
            parameterMap.forEach((key, value) -> paramMap.put(key, String.join(",", value)));
            sb.append("[ URL请求参数 ").append(JSONObject.toJSONString(paramMap)).append(" ] ");
        }

        // @RequestBody请求参数
        if (method.equalsIgnoreCase(RequestMethod.POST.toString())) {
            Object[] args = joinPoint.getArgs();
            if (!(args[0] instanceof ServletRequest || args[0] instanceof MultipartFile || args[0] instanceof ServletResponse)) {
                sb.append("[ RequestBody请求参数 ").append(JSONObject.toJSONString(args[0])).append(" ] ");
            }
        }

        log.info(String.format("[ Controller请求信息 ] %s", sb));

        Object obj;
        try {
            obj = joinPoint.proceed();
        } catch (Throwable throwable) {
            long endTime = System.currentTimeMillis();
            log.info(String.format("[ Controller响应错误信息 ] [ %s ] 耗时 %d ms.", throwable.getMessage(), (endTime - startTime)));

            // 销毁请求仓库
            RequestStore.destroy();
            throw throwable;
        }
        long endTime = System.currentTimeMillis();
        log.info(String.format("[ Controller响应信息 ] %s 耗时 %d ms.", JSONObject.toJSONString(obj), (endTime - startTime)));

        // 销毁请求仓库
        RequestStore.destroy();
        return obj;
    }

    @Around("execution(* com.aegonthtf.sis..service..*(..))" +
            "|| execution(* com.baomidou.mybatisplus.extension.service..*(..))")
    public Object doServicePointAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();

        // 请求方法描述
        Signature signature = joinPoint.getSignature();
        String methodDesc = signature.getDeclaringTypeName() + "." + signature.getName();
        sb.append(methodDesc);

        // 请求参数
        Object[] args = joinPoint.getArgs();
        if (ArrayUtils.isNotEmpty(args)) {
            sb.append(" [ 请求参数 ] ");
            for(int i = 0; i<args.length;i++) {
                if(args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile){
                    continue;
                }
                sb.append(JSONObject.toJSONString(args[i]));
            }

        }

        log.info(String.format("[ Service请求信息 ] %s", sb));

        Object obj;
        try {
            obj = joinPoint.proceed();
        } catch (Throwable throwable) {
            long endTime = System.currentTimeMillis();
            log.info(String.format("[ Service响应错误信息 ] [ %s ] 耗时 %d ms.", throwable.getMessage(), (endTime - startTime)));

            // 销毁请求仓库
            RequestStore.destroy();
            throw throwable;
        }
        long endTime = System.currentTimeMillis();
        log.info(String.format("[ Service响应信息 ] %s 耗时 %d ms.", JSONObject.toJSONString(obj), (endTime - startTime)));

        return obj;
    }
}
